﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using BlobNet.Base.DataProviders;

namespace BlobNet.Base.Collections.List
{
    [DebuggerDisplay("Count = {Count}")]
    //TODO:quit comment [DebuggerStepThrough]
    [Serializable]
    internal abstract class PersistentListBase<TType> : BlobPartCollection, IList<TType>, ICollection
    {
        #region Attributes

        [NonSerialized] 
        private object _syncRoot;

        #endregion Atttributes

        #region Constructors


        protected PersistentListBase(BlobDomain blobDomain, Guid collectionId)
            : base(blobDomain, collectionId)
        {
            Version = 0;
        }

       

        protected PersistentListBase(BlobDomain blobDomain, Guid collectionId, IEnumerable<TType> source)
            : this(blobDomain, collectionId)
        {
            AddRange(source);
        }

       

        protected PersistentListBase()
        {
        }

        #endregion Constructors

        #region Properties

        public int Capacity
        {
            get { throw new InvalidOperationException("Capacity is limited by storage"); }
            set { throw new InvalidOperationException("Capacity cannot be set"); }
        }

        public bool IsFixedSize
        {
            get { return false; }
        }

        public bool IsReadOnly
        {
            get { return false; }
        }

        protected int Version
        {
            get { return GetStatusValue<int>("Version"); }
            set { SetStatusValue("Version", value); }
        }

        #region ICollection Members

        bool ICollection.IsSynchronized
        {
            get { return false; }
        }

        object ICollection.SyncRoot
        {
            get
            {
                if (_syncRoot == null)
                    Interlocked.CompareExchange(ref _syncRoot, new object(), null);
                return _syncRoot;
            }
        }

        #endregion

        #region IList<TType> Members

        public int Count
        {
            get { return DataProvider.GetCollectionCount(Domain.EntityId, EntityId); }
        }

        bool ICollection<TType>.IsReadOnly
        {
            get { return false; }
        }

        #endregion

        #endregion Properties

        #region Public Methods

        #region ICollection Members

        public void CopyTo(Array array, int arrayIndex)
        {
            //TODO: Diego Validate behaviour Please
            throw new InvalidOperationException("This operation is not supported for performance reasons");
        }

        #endregion

        #region IList<TType> Members

        public abstract void Add(TType item);


        public void Clear()
        {
            if (Count > 0)
            {
                DataProvider.RemoveCollection(Domain.EntityId, EntityId);
            }
            ++Version;
        }

        public abstract bool Contains(TType item);


        public void CopyTo(TType[] array, int arrayIndex)
        {
            //TODO: NOT IMPLEMENTED PROPERLY arrayindex

            for (int i = 0; i < Count; i++)
            {
                array[i] = this[i];
            }
        }

        IEnumerator<TType> IEnumerable<TType>.GetEnumerator()
        {
            return new BlobListEnumerator(this);
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return new BlobListEnumerator(this);
        }

        public abstract int IndexOf(TType item);

        public abstract void Insert(int index, TType item);


        public bool Remove(TType item)
        {
            bool result = false;
            if (item != null)
            {
                int index = IndexOf(item);
                if (index >= 0)
                {
                    RemoveAt(index);
                    result = true;
                }
            }
            return result;
        }

        public virtual void RemoveAt(int index)
        {
            if ((uint) index >= (uint) Count)
                throw new ArgumentOutOfRangeException();

            DataProvider.RemoveEntityFromCollection(Domain.EntityId, EntityId, index);

            ++Version;
        }

        #endregion

        public void CopyTo(TType[] array)
        {
            CopyTo(array, 0);
        }

        public void CopyTo(int index, TType[] array, int arrayIndex, int count)
        {
            //TODO: Diego Validate behaviour Please
            throw new InvalidOperationException("This operation is not supported for performance reasons");
        }

        public void AddRange(IEnumerable<TType> collection)
        {
            InsertRange(Count, collection);
        }

        public abstract void InsertRange(int index, IEnumerable<TType> collection);

        public void Sort()
        {
            //TODO:NOT TESTED
            var array = new TType[Count];

            CopyTo(array);

            Array.Sort(array, 0, array.Length, null);

            Clear();

            AddRange(array);
        }

        #endregion Public Methods

        #region Indexer

        public abstract TType this[int index] { get; set; }

        #endregion Indexer

        #region Nested type: BlobListEnumerator

        [Serializable]
        public struct BlobListEnumerator : IEnumerator<TType>
        {
            #region Attributes

            private readonly PersistentListBase<TType> list;
            private readonly int version;
            private TType current;
            private int index;

            #endregion Attributes

            #region Constructors

            internal BlobListEnumerator(PersistentListBase<TType> list)
            {
                this.list = list;
                index = 0;
                version = list.Version;
                current = default(TType);
            }

            #endregion Constructors

            #region IEnumerator<TType> Members

            public TType Current
            {
                get { return current; }
            }

            object IEnumerator.Current
            {
                get
                {
                    if (index == 0 || index == list.Count + 1)
                        throw new InvalidOperationException("index out of bounds");
                    return Current;
                }
            }

            public void Dispose()
            {
            }

            public bool MoveNext()
            {
                PersistentListBase<TType> list = this.list;
                if (version != list.Version || (uint) index >= (uint) list.Count)
                    return MoveNextRare();
                current = list[index];
                ++index;
                return true;
            }

            void IEnumerator.Reset()
            {
                if (version != list.Version)
                    throw new InvalidOperationException("The version of the list is not valid");
                index = 0;
                current = default(TType);
            }

            #endregion

            private bool MoveNextRare()
            {
                if (version != list.Version)
                    throw new InvalidOperationException("The version of the list is not valid");
                index = list.Count + 1;
                current = default(TType);
                return false;
            }
        }

        #endregion

        #region Implementation of IDisposable

        public void Dispose()
        {
            //TODO
        }

        #endregion
    }
}